#ifndef _FRUSTUM_H_
#define _FRUSTUM_H_


#include "plane.h"
#include "mat4x4.h"

namespace math
{


class Frustum
{
public:

    enum FrustumTest
    {
	    FrustumInside,
	    FrustumPartial,
	    FrustumOutside,
	    FrustumUnknown = 0xffff
    };

    enum PlaneEnum
    {
	    LeftPlane       = 0,
	    RightPlane      = 1,

	    TopPlane        = 2,
	    BottomPlane     = 3,

	    NearPlane       = 4,
	    FarPlane        = 5,
    
        PlaneCount,
        PlaneUnknown = 0xffff
    };


public:
    /**
     * create frustum
     */
					Frustum();

                    
    /**
     * create frustum from another frustum
     */
					Frustum(const Frustum& _Frust);
    /**
     * delete frustum
     */
	    			~Frustum();

    /**
     * Calculate frustum
     * @param _mMat         World Mat4x4 of camera
     * @param _mProj        Projection Mat4x4 of camera
     * @param _bNormalize   Normalize plane normals (necessary for some operations)
     * @param _bInvert      Should we invert the normals (wrong coordinate system ?)
     */
	bool			CalculateFrustrum(const Mat4x4& _mMat, const Mat4x4& _mProj, bool _bNormalize, bool _bInvert);

    /**
     * Test an Axis Aligned Bounding Box against the frustum
     * @param _vMin         Low corner of box
     * @param _vMax         High corner of box
     * @returns             Fully inside, partially inside or outside ?
     */   
    FrustumTest		TestAABB(const Vec3& _vMin, const Vec3& _vMax) const;

    /**
     * Test a point against the frustum
     * @param _vPos         Point
     * @returns             Fully inside, partially inside or outside ?
     */   
    FrustumTest		TestPoint(const Vec3& _vPos)  const;

    /**
     * Test a sphere against the frustum
     * @param _vPos         Point
     * @param _fRad         Radius of sphere
     * @returns             Fully inside, partially inside or outside ?
     */   
	FrustumTest		TestSphere(const Vec3& _vPos, float _fRad)  const;

    /**
     * Test an OBB. Not implemented
     * @param _vPos         Point
     * @returns             Fully inside, partially inside or outside ?
     */   
    FrustumTest		TestOBB(Vec3 _vPoint[])  const;

    /**
     * Clone a frustum from another frustum
     */
     void            Clone(const Frustum& _Frust);


     Plane*          GetPlane(PlaneEnum _Plane)         { return &m_PlaneA[_Plane]; };
     const Plane*    GetPlane(PlaneEnum _Plane) const    { return &m_PlaneA[_Plane]; };
protected:	// functions
	inline void			SwapIfNeg(float _fSign, float _fFrom1, float _fFrom2, float& _fTo1, float&_fTo2) const;
	inline float		DistanceToPoint(const Plane* _pPlane, const Vec3* _pPoint) const;

    
    Plane			    m_PlaneA[PlaneCount];
};

} // end namespace math

#endif // _FRUSTUM_H_